#include "core.h"

Core::Core()
{
	m_Input = 0;
	soundTask = 0;
}

Core::Core(const Core& other)
{
}


Core::~Core()
{
}

bool Core::init()
{
	int screenWidth, screenHeight;
	
	t.start();

	// init the width and height of the screen to zero before sending the variables into the function.
	screenWidth = 0;
	screenHeight = 0;

	// init the windows api.
	initWindows(screenWidth, screenHeight);

	// Create the input object.  This object will be used to handle reading the keyboard input from the user.
	m_Input = new InputManager();
	if(!m_Input) {
		return false;
	}

	soundTask = new SoundTask();
	soundTask->init();
	
	//connect signals and slots
	m_Input->getKeyboard().sigKeyDown().connect(this, &Core::keyDown);
	m_Input->getKeyboard().sigKeyDown().connect(soundTask, &SoundTask::keyDown);
		
	return true;
}

void Core::Shutdown()
{
	t.stop();

	if(soundTask) {
		delete soundTask;
		soundTask = 0;
	}

	// Release the input object.
	if(m_Input) {
		delete m_Input;
		m_Input = 0;
	}

	// Shutdown the window.
	ShutdownWindows();
	
	return;
}

void Core::Run()
{
	MSG msg;
	bool result;

	// init the message structure.
	ZeroMemory(&msg, sizeof(MSG));
	
	// Loop until there is a quit message from the window or the user.
	done = false;
	
	double lastTime = t.getElapsedTimeInMilliSec();
	double currentTime = lastTime;
	double delta;

	while(!done)
	{
		currentTime = t.getElapsedTimeInMilliSec();
		delta = currentTime - lastTime;
		lastTime = currentTime;
		
		// Handle the windows messages.
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		// If windows signals to end the application then exit
		if(msg.message == WM_QUIT) {
			done = true;
		} else {
			// Otherwise do the frame processing.
			result = frame(delta);
			if(!result)	{
				done = true;
			}
		}

	}
	return;
}

void Core::keyDown(const InputEvent& e)
{
	if(e.id == VK_ESCAPE)
	{
		done = true; //quit
	}
}

bool Core::frame(double delta)
{
	//update task
	if(soundTask)
		soundTask->execute(delta);

	return true;
}

LRESULT CALLBACK Core::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
	bool mouseLeft = (wparam & 0x0001);
	bool mouseRight= (wparam & 0x0002)>>1;
	bool mouseMiddle= (wparam & 0x0010)>>4;
	bool ctrl = (wparam & 0x0008)>>3;
	bool shift = (wparam & 0x0004)>>2;
	bool alt = GetKeyState(VK_MENU)<0;
	InputState state(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), mouseLeft, mouseRight, mouseMiddle, ctrl, shift, alt);

	switch(umsg)
	{
		// Check if a key has been pressed on the keyboard.
		case WM_KEYDOWN:
		{
			//emit key down signal
			m_Input->getKeyboard().sigKeyDown().emit( InputEvent((unsigned int) wparam, state ));
			return 0;
		}

		// Check if a key has been released on the keyboard.
		case WM_KEYUP:
		{
			m_Input->getKeyboard().sigKeyUp().emit( InputEvent((unsigned int) wparam, state ));
			return 0;
		}

		case WM_LBUTTONDOWN:
		{
			m_Input->getMouse().sigKeyDown().emit( InputEvent((unsigned int) wparam, state ));	
			return 0;
		}

		case WM_RBUTTONDOWN:
		{
			m_Input->getMouse().sigKeyDown().emit( InputEvent((unsigned int) wparam, state ));	
			return 0;
		}

		case WM_MBUTTONDOWN:
		{
			m_Input->getMouse().sigKeyDown().emit( InputEvent((unsigned int) wparam, state ));	
			return 0;
		}

		case WM_LBUTTONUP:
		{
			m_Input->getMouse().sigKeyUp().emit( InputEvent((unsigned int) wparam, state ));	
			return 0;
		}

		case WM_RBUTTONUP:
		{
			m_Input->getMouse().sigKeyUp().emit( InputEvent((unsigned int) wparam, state ));	
			return 0;
		}

		case WM_MBUTTONUP:
		{
			m_Input->getMouse().sigKeyUp().emit( InputEvent((unsigned int) wparam, state ));	
			return 0;
		}

		case WM_MOUSEMOVE:
		{
			m_Input->getMouse().sigMove().emit( InputEvent((unsigned int) wparam, state ));	
		}

		// Any other messages send to the default message handler as our application won't make use of them.
		default:
		{
			return DefWindowProc(hwnd, umsg, wparam, lparam);
		}
	}
}

void Core::initWindows(int& screenWidth, int& screenHeight)
{
	WNDCLASSEX wc;
	DEVMODE dmScreenSettings;
	int posX, posY;


	// Get an external pointer to this object.
	ApplicationHandle = this;

	// Get the instance of this application.
	m_hinstance = GetModuleHandle(NULL);

	// Give the application a name.
	m_applicationName = L"FMOD Design Demo";

	// Setup the windows class with default settings.
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = m_hinstance;
	wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
	wc.hIconSm = wc.hIcon;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = m_applicationName;
	wc.cbSize = sizeof(WNDCLASSEX);
	
	// Register the window class.
	RegisterClassEx(&wc);

	// Determine the resolution of the clients desktop screen.
	screenWidth  = GetSystemMetrics(SM_CXSCREEN);
	screenHeight = GetSystemMetrics(SM_CYSCREEN);
	
	DWORD params =  WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
	
	// Setup the screen settings depending on whether it is running in full screen or in windowed mode.
	if(FULL_SCREEN)	{
		// If full screen set the screen to maximum size of the users desktop and 32bit.
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth  = (unsigned long)screenWidth;
		dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
		dmScreenSettings.dmBitsPerPel = 32;			
		dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		// Change the display settings to full screen.
		ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);

		// Set the position of the window to the top left corner.
		posX = posY = 0;
	} else {
		// If windowed then set it to 800x600 resolution.
		screenWidth  = 1280;
		screenHeight = 800;

		// Place the window in the middle of the screen.
		posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth)  / 2;
		posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;

		RECT rect;
		rect.top = posY;
		rect.left = posX;
		rect.bottom = posY+screenHeight;
		rect.right = posX+screenWidth;
		params =  params | WS_OVERLAPPEDWINDOW;
		AdjustWindowRectEx(&rect, params, false, 0);
	}

	// Create the window with the screen settings and get the handle to it.
	m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName, params,
				posX, posY, screenWidth, screenHeight, NULL, NULL, m_hinstance, NULL);

	// Bring the window up on the screen and set it as main focus.
	ShowWindow(m_hwnd, SW_SHOW);
	SetForegroundWindow(m_hwnd);
	SetFocus(m_hwnd);

	// Hide the mouse cursor.
	ShowCursor(true);

	return;
}

void Core::ShutdownWindows()
{
	// Show the mouse cursor.
	ShowCursor(true);

	// Fix the display settings if leaving full screen mode.
	if(FULL_SCREEN)	{
		ChangeDisplaySettings(NULL, 0);
	}

	// Remove the window.
	DestroyWindow(m_hwnd);
	m_hwnd = NULL;

	// Remove the application instance.
	UnregisterClass(m_applicationName, m_hinstance);
	m_hinstance = NULL;

	// Release the pointer to this class.
	ApplicationHandle = NULL;

	return;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
	switch(umessage)
	{
		// Check if the window is being destroyed.
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;

		// Check if the window is being closed.
		case WM_CLOSE:
			PostQuitMessage(0);		
			return 0;

		// All other messages pass to the message handler in the system class.
		default:
			return ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
	}
}
